namespace EFMLEditor.Internal
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;

    public class MacroContext
    {
        internal readonly Dictionary<string, string> variables = new Dictionary<string, string>();

        private readonly Dictionary<string, Delegate> functions = new Dictionary<string, Delegate>();

        public void AddVar<T>(string name, T value)
        {
            this.variables.Add(name, value.ToString());
        }

        public void AddFunc(string name, Delegate d)
        {
            if(!functions.ContainsKey(name))
                this.functions.Add(name, d);
        }

        public Delegate GetFunc(string name)
        {
            return this.functions[name];
        }
    }

    public class Macro
    {
        private readonly MacroContext context = new MacroContext();

        public MacroContext GetContext()
        {
            return this.context;
        }

        protected virtual void OnResolveFunc(ResolveFuncEventArgs e)
        {
            this.context.GetFunc(e.Name).DynamicInvoke(e.Arguments);
        }

        public Macro(MacroContext c)
        {
            this.context = c;
        }

        public Macro()
        {
        }

        public static void Execute(string s, MacroContext mc)
        {
            if (!s.Equals(""))
            {
                var m = new Macro(mc);
                m.AddStandardFuncs();
                var ss = m.replaceVars(s);
                m.Resolve(ss);
            }
        }

        public void AddStandardFuncs()
        {
            this.context.AddFunc("call", new Action<string>(s => Process.Start(s)));
            this.context.AddFunc("alert", new Action<string>(s => MessageBox.Show(s)));
            this.context.AddFunc("delete", new Action<string>(File.Delete));
            this.context.AddFunc("copy", new Action<string, string>(File.Copy));
        }

        private string replaceVars(string input)
        {
            return this.context.variables.Aggregate(
                input, (current, variable) => current.Replace("$(" + variable.Key + ")", variable.Value));
        }

        public void Resolve(string input)
        {
            this.replaceVars(input);

            string[] spl = input.Split('\r');
            foreach (var s in spl)
            {
                var spl2 = new List<string>(s.Split(' '));
                var e = new ResolveFuncEventArgs();
                e.Name = spl2[0];
                spl2.RemoveAt(0);

                e.Arguments = new List<object>(string.Join(" ", spl2).Split(',')).ToArray();

                this.OnResolveFunc(e);
            }
        }

        public class ResolveFuncEventArgs : EventArgs
        {
            public string Name;

            public object[] Arguments;
        }
    }
}